package org.zoolu.sound.codec;



/** G.711 codec.
  * <p>
  * This class provides methods for u-law, A-law and linear PCM conversions.
  * <p>
  * This implementation is based on the ANSI-C language reference implementations
  * of the CCITT (International Telegraph and Telephone Consultative Committee)
  * G.711, G.721 and G.723 voice compressions, provided by Sun Microsystems, Inc.
  * <p>
  * Acknowledgement to Sun Microsystems, Inc. for having released the original
  * ANSI-C source code to the public domain.
  */
public class G711 {
	
	static final int SIGN_BIT=0x80;    // Sign bit for a A-law byte.
	static final int QUANT_MASK=0xf;   // Quantization field mask.
	static final int NSEGS=8;          // Number of A-law segments.
	static final int SEG_SHIFT=4;      // Left shift for segment number.
	static final int SEG_MASK=0x70;    // Segment field mask.
  
	static final int[] seg_end={  0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF  };
  
	// copy from CCITT G.711 specifications
	
	/** u- to A-law conversions */
	static final int[] _u2a={
	  	1,	1,	2,	2,	3,	3,	4,	4,
	  	5,	5,	6,	6,	7,	7,	8,	8,
	  	9,	10,	11,	12,	13,	14,	15,	16,
	  	17,   18,	19,	20,	21,	22,	23,	24,
	  	25,	27,	29,	31,	33,	34,	35,	36,
	  	37,	38,	39,	40,	41,	42,	43,	44,
	  	46,	48,	49,	50,	51,	52,	53,	54,
	  	55,	56,	57,	58,	59,	60,	61,	62,
	  	64,	65,	66,	67,	68,	69,	70,	71,
	  	72,	73,	74,	75,	76,	77,	78,	79,
	  	81,	82,	83,	84,	85,	86,	87,	88,
	  	89,	90,	91,	92,	93,	94,	95,	96,
	  	97,	98,	99,	100,	101,	102,	103,	104,
	  	105,	106,	107,	108,	109,	110,	111,	112,
	  	113,	114,	115,	116,	117,	118,	119,	120,
	  	121,	122,	123,	124,	125,	126,	127,	128  };
	  	
	/** A- to u-law conversions */
	static final int[] _a2u={
	  	1,	3,	5,	7,	9,	11,	13,	15,
	  	16,	17,	18,	19,	20,	21,	22,	23,
	  	24,	25,	26,	27,	28,	29,	30,	31,
	  	32,	32,	33,	33,	34,	34,	35,	35,
	  	36,	37,	38,	39,	40,	41,	42,	43,
	  	44,	45,	46,	47,	48,	48,	49,	49,
	  	50,	51,	52,	53,	54,	55,	56,	57,
	  	58,	59,	60,	61,	62,	63,	64,	64,
	  	65,	66,	67,	68,	69,	70,	71,	72,
	  	73,	74,	75,	76,	77,	78,	79,	79,
	  	80,	81,	82,	83,	84,	85,	86,	87,
	  	88,	89,	90,	91,	92,	93,	94,	95,
	  	96,	97,	98,	99,	100,	101,	102,	103,
	  	104,	105,	106,	107,	108,	109,	110,	111,
	  	112,	113,	114,	115,	116,	117,	118,	119,
	  	120,	121,	122,	123,	124,	125,	126,	127  };
  
  
	static int search(int val,	int[] table) {
		for (int i=0; i<table.length; i++) if (val<=table[i]) return i;
		return table.length;
	}

  
	/** Converts a 16-bit linear PCM value to 8-bit A-law.
	  *
	  * It accepts an 16-bit integer and encodes it as A-law data.
	  *
	  *   Linear Input Code    Compressed Code
	  *   -----------------    ---------------
	  *   0000000wxyza         000wxyz
	  *   0000001wxyza         001wxyz
	  *   000001wxyzab         010wxyz
	  *   00001wxyzabc         011wxyz
	  *   0001wxyzabcd         100wxyz
	  *   001wxyzabcde         101wxyz
	  *   01wxyzabcdef         110wxyz
	  *   1wxyzabcdefg         111wxyz
	  *
	  * For further information see John C. Bellamy's Digital Telephony, 1982,
	  * John Wiley &amp; Sons, pps 98-111 and 472-476.
	  */
	public static int linear2alaw(int pcm_val) {
		// 2's complement (16-bit range)
		int mask;
		int seg;
		//unsigned char aval;
		int aval;
  
	  	if (pcm_val>=0)
	  	{   mask=0xD5; // sign (7th) bit = 1
	  	}
		else {
			mask=0x55; // sign bit = 0
			pcm_val=-pcm_val-8;
	  	}
	  
	  	// Convert the scaled magnitude to segment number.
	  	seg=search(pcm_val,seg_end);
	  
	  	// Combine the sign, segment, and quantization bits.
	  	if (seg>=8) // out of range, return maximum value.
			return (0x7F^mask);
	  	else
	  	{  aval=seg<<SEG_SHIFT;
			if (seg<2) aval|=(pcm_val>>4)&QUANT_MASK;
	  	   else aval|=(pcm_val>>(seg+3))&QUANT_MASK;
			return (aval^mask);
	  	}
	}
  
	/** Converts an A-law value to 16-bit linear PCM
	  */
	//public static int alaw2linear(unsigned char a_val)
	public static int alaw2linear(int a_val) {
		int t;
		int seg;
  	   a_val^=0x55;   
	  	t=(a_val&QUANT_MASK)<<4;
	  	//seg=((unsigned)a_val&SEG_MASK)>>SEG_SHIFT;
		seg=(a_val&SEG_MASK)>>SEG_SHIFT;
	  	switch (seg)
	  	{  case 0:
				t+=8;
				break;
	  	   case 1:
				t+=0x108;
				break;
			default:
	  	      t+=0x108;
	  	      t<<=seg-1;
  	   }
  	   return ((a_val&SIGN_BIT)!=0)? t : -t;
	}
  
  
	/** Bias for linear code. */
	public static final int BIAS=0x84;


	/** Converts a linear PCM value to u-law
	  *
	  * In order to simplify the encoding process, the original linear magnitude
	  * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
	  * (33 - 8191). The result can be seen in the following encoding table:
	  *
	  *   Biased Linear Input Code   Compressed Code
	  *   ------------------------   ---------------
	  *   00000001wxyza              000wxyz
	  *   0000001wxyzab              001wxyz
	  *   000001wxyzabc              010wxyz
	  *   00001wxyzabcd              011wxyz
	  *   0001wxyzabcde              100wxyz
	  *   001wxyzabcdef              101wxyz
	  *   01wxyzabcdefg              110wxyz
	  *   1wxyzabcdefgh              111wxyz
	  *
	  * Each biased linear code has a leading 1 which identifies the segment
	  * number. The value of the segment number is equal to 7 minus the number
	  * of leading 0's. The quantization interval is directly available as the
	  * four bits wxyz.  The trailing bits (a - h) are ignored.
	  *
	  * Ordinarily the complement of the resulting code word is used for
	  * transmission, and so the code word is complemented before it is returned.
	  *
	  * For further information see John C. Bellamy's Digital Telephony, 1982,
	  * John Wiley &amp; Sons, pps 98-111 and 472-476.
	  */
	public static int linear2ulaw(int pcm_val) {
		// 2's complement (16-bit range)
		int mask;
		int seg;
		//unsigned char uval;
		int uval;
  
  	   // Get the sign and the magnitude of the value.
		if (pcm_val<0)
  	   {  pcm_val=BIAS-pcm_val;
			mask=0x7F;
  	   }
  	   else
  	   {  pcm_val+=BIAS;
  	      mask=0xFF;
  	   }
  	   // Convert the scaled magnitude to segment number.
  	   seg=search(pcm_val,seg_end);
  
  	   // Combine the sign, segment, quantization bits; and complement the code word.

	  	if (seg>=8) return (0x7F^mask); // out of range, return maximum value.
	  	else
	  	{  uval=(seg<<4) | ((pcm_val>>(seg+3)) & 0xF);
			return (uval^mask);
  	   }
	}
  
	/** Converts a u-law value to 16-bit linear PCM.
	  *
	  * First, a biased linear code is derived from the code word. An unbiased
	  * output can then be obtained by subtracting 33 from the biased code.
	  *
	  * Note that this function expects to be passed the complement of the
	  * original code word. This is in keeping with ISDN conventions.
	  */
	//public static int ulaw2linear(unsigned char u_val)
	public static int ulaw2linear(int u_val) {
		int t;
	  	// Complement to obtain normal u-law value.
	  	u_val=~u_val;
  	   // Extract and bias the quantization bits. Then shift up by the segment number and subtract out the bias.
	  	t=((u_val&QUANT_MASK)<<3) + BIAS;
	  	//t<<=((unsigned)u_val&SEG_MASK)>>SEG_SHIFT;
		t<<=(u_val&SEG_MASK)>>SEG_SHIFT;
	  
	  	return ((u_val&SIGN_BIT)!=0)? (BIAS-t) : (t-BIAS);
  }

  
	/** A-law to u-law conversion.
	  */
	//public static int alaw2ulaw(unsigned char aval)
	public static int alaw2ulaw(int aval) {
		aval&=0xff;
  	   return ((aval & 0x80)!=0)? (0xFF^_a2u[aval^0xD5]) : (0x7F^_a2u[aval^0x55]);
	}

  
	/** u-law to A-law conversion.
	  */
	//public static int ulaw2alaw(unsigned char uval)
	public static int ulaw2alaw(int uval) {
		uval&=0xff;
  	   return ((uval&0x80)!=0)? (0xD5^(_u2a[0xFF^uval]-1)) : (0x55^(_u2a[0x7F^uval]-1));
	}

	
	/** PCM ecoder/decoder tests. */
	/*public static void main(String[] args) {
		
		for (int i=0; i<0xFF; i++) {
			int pcmu,pcma,linear;
			pcmu=i;
			System.out.print("  pcmu:"+pcmu);
			pcma=ulaw2alaw(pcmu);
			System.out.print("  pcma:"+pcma);
			pcmu=alaw2ulaw(pcma);
			System.out.print("  pcmu:"+pcmu);
			linear=alaw2linear(pcma);
			System.out.print("  linear:"+linear);
			pcma=linear2alaw(linear);
			System.out.print("  pcma:"+pcma);
			linear=ulaw2linear(pcmu);
			System.out.print("  linear:"+linear);
			pcmu=linear2ulaw(linear);
			System.out.print("  pcmu:"+pcmu);
			System.out.println(" .");
			if (i%20==19)
			try {  System.in.read();  } catch (Exception e) {}
		}
	}*/
}
